﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using linq = Microsoft.Linq.Expressions;
using Bling.Util;
using Bling.Reactive;

namespace Bling.Core {
  using Bling.DSL;
  using Bling.Ops;

  public class ObservableCollectionBl<E,EBRAND> : Brand<ObservableCollection<E>, ObservableCollectionBl<E,EBRAND>> 
    where EBRAND : Brand<E,EBRAND> {
    public ObservableCollectionBl() : base(new ObservableCollection<E>()) { }
    public ObservableCollectionBl(Expr<ObservableCollection<E>> Provider) : base(Provider) { }

    public static implicit operator ObservableCollectionBl<E, EBRAND>(Expr<ObservableCollection<E>> e) {
      return new ObservableCollectionBl<E, EBRAND>(e);
    }
    public static implicit operator ObservableCollectionBl<E, EBRAND>(ObservableCollection<E> e) {
      return new Constant<ObservableCollection<E>>(e);
    }


  }



  public interface ICollectionBl<E, EBRAND> where  EBRAND : ICanBind<EBRAND> {
    IList<EBRAND> AddMany { set; }
    void Add(params EBRAND[] value);
    EBRAND AddOne { set; }
  }
  /// <summary>
  /// Bling wrapper around collections.
  /// </summary>
  /// <typeparam name="E">Collection element type.</typeparam>
  /// <typeparam name="EBRAND">Bling type of collection element.</typeparam>
  /// <typeparam name="T">Original collection type.</typeparam>
  /// <typeparam name="BRAND">Bling type of collection.</typeparam>
  public abstract partial class CollectionBl<E, EBRAND, T, BRAND> : Brand<T, BRAND>, ICollectionBl<E, EBRAND>
    where BRAND : CollectionBl<E, EBRAND, T, BRAND>
    where T : IList, ICollection, /* IList<E>, ICollection<E>, IEnumerable<E>, */ IEnumerable
    where EBRAND : ICanBind<EBRAND> { 
    public CollectionBl(Expr<T> Underlying) : base(Underlying) { }
    public CollectionBl() : base() { }
    /// <summary>
    /// Add multiple elements to this collection using setter syntax. Useful for intilizers.
    /// </summary>
    public IList<EBRAND> AddMany {
      set {
        foreach (var e in value) 
          this.CurrentValue.Add(CanWrap<EBRAND, E>.From(e).CurrentValue);
        true.Assert();
      }
    }
    /// <summary>
    /// Add elements to this collection.
    /// </summary>
    public void Add(params EBRAND[] value) { AddMany = value; }
    /// <summary>
    /// Add one element to this collection using setter syntax. Useful for initializers.
    /// </summary>
    public EBRAND AddOne {
      set { this.CurrentValue.Add(CanWrap<EBRAND,E>.From(value).CurrentValue); }
    }
    public EBRAND this[IntBl Idx] {
      get {
        return Idx.Combine<T, BRAND>(this).Map<E,EBRAND>((idx, coll) => {
          return (E)coll[idx];
        });
      }
    }

  }
  /// <summary>
  /// For Bling wrappers around implicitly typed collections that don't require unique brands. 
  /// </summary>
  /// <typeparam name="E">Collection element type.</typeparam>
  /// <typeparam name="EBRAND">Bling type of collection element.</typeparam>
  /// <typeparam name="T">Original collection type.</typeparam>
  public class CollectionBl<E, EBRAND, T> : CollectionBl<E, EBRAND, T, CollectionBl<E, EBRAND, T>>
    where T : IList, ICollection, IEnumerable
    where EBRAND : ICanBind<EBRAND> {
    public CollectionBl(Expr<T> Underlying) : base(Underlying) { }
    public CollectionBl() : base() { }
    public static implicit operator CollectionBl<E, EBRAND, T>(Expr<T> target) { return new CollectionBl<E, EBRAND, T>((target)); }
    public static implicit operator CollectionBl<E, EBRAND, T>(T target) { return (new Constant<T>(target)); }
  }
  /// <summary>
  /// For Bling wrappers around explicitly typed collections that don't require unique brands.
  /// </summary>
  /// <typeparam name="E">Collection element type.</typeparam>
  /// <typeparam name="EBRAND">Bling type of collection element.</typeparam>
  /// <typeparam name="T">Original collection type.</typeparam>
  public class CollectionXBl<E, EBRAND, T> : CollectionBl<E, EBRAND, T, CollectionBl<E, EBRAND, T>>
    where T : IList, ICollection, IList<E>, ICollection<E>, IEnumerable<E>, IEnumerable, new()
    where EBRAND : ICanBind<EBRAND> {
    public CollectionXBl(Expr<T> Underlying) : base(Underlying) { }
    public CollectionXBl() : this(new Constant<T>(new T())) { }
    public static implicit operator CollectionXBl<E, EBRAND, T>(Expr<T> target) { return new CollectionXBl<E, EBRAND, T>((target)); }
    public static implicit operator CollectionXBl<E, EBRAND, T>(T target) { return (new Constant<T>(target)); }
    public static implicit operator CollectionXBl<E, EBRAND, T>(EBRAND[] values) {
      return new CollectionXBl<E, EBRAND, T>() {
        AddMany = values,
      };
    }
  }
  public interface IPrimitiveCollectionBl<E, EBRAND> : ICollectionBl<E, EBRAND> where EBRAND : ICanBind<EBRAND> {
    void InitElements(IList<EBRAND> Backing);
    Action RefreshAction(IList<EBRAND> Backing);
  }
  public interface IListBl<T> {
    int Count { get; }
    T this[IntBl Idx] { get; }
  }



  /// <summary>
  /// Any minimum/maximum property pair.
  /// </summary>
  public abstract class RangeBl<BRAND, MONO> where BRAND : Brand<BRAND>, INumeric<BRAND,MONO> 
    where MONO : Brand<MONO>, INumeric<MONO, MONO> {
    /// <summary>
    /// Maximum value of this range set.
    /// </summary>
    public abstract BRAND Maximum { get; set; }
    /// <summary>
    /// Minimum value of this range set. 
    /// </summary>
    public abstract BRAND Minimum { get; set; }
    public void Bind(RangeBl<BRAND, MONO> other) {
      Maximum.Bind = other.Maximum; Minimum.Bind = other.Minimum;
    }
  }
  /// <summary>
  /// Default range implementation. 
  /// </summary>
  public class RangeBl : RangeBl<DoubleBl, DoubleBl> {
    public override DoubleBl Maximum { get; set; }
    public override DoubleBl Minimum { get; set; }
    public StringBl ToStringBl(DoubleBl d) {
      return Minimum.ToStringBl(d) + " - " + Maximum.ToStringBl(d);
    }
    public override string ToString() {
      return Minimum + " - " + Maximum;
    }
  }
}
